package com.wei.web.demo.user.security;

import com.wei.web.demo.user.security.filter.NothingAuthenticationFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
@ConditionalOnProperty(value = "fas.enable", havingValue = "no", matchIfMissing = true)
@Slf4j
public class SecurityConfiguration {

//    @Bean
    public NothingAuthenticationFilter nothingAuthenticationFilter() {
        return new NothingAuthenticationFilter("/**", authenticationManager());
    }


    public AuthenticationManager authenticationManager() {
        ProviderManager providerManager = new ProviderManager(new DummyAuthenticationProvider());
        providerManager.setEraseCredentialsAfterAuthentication(false);
        return providerManager;
    }

    @Bean
    public DummyAuthenticationProvider dummyAuthenticationProvider() {
        return new DummyAuthenticationProvider();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> {
            web.ignoring().requestMatchers(new AntPathRequestMatcher("/web/demo/order/**"));
            web.httpFirewall(new SuppressStackTraceHttpFirewall());
        };
    }



    @Bean("webSecurityFilterChain")
    @Order(1200)
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable);
        http.headers(AbstractHttpConfigurer::disable);
        http.sessionManagement(sessionManagementCustomizer -> sessionManagementCustomizer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        http.cors(AbstractHttpConfigurer::disable);

        // Authentication and Authorization
        // filter add here must be annotated @Component, otherwise WebSecurity.ignoring() will not work. Refer to: https://github.com/spring-projects/spring-security/issues/3958
        http.authorizeRequests(a -> a.requestMatchers("/web/demo/ticket/**").authenticated()
                .anyRequest().denyAll());
        http.authenticationProvider(dummyAuthenticationProvider());
        http.addFilterAfter(nothingAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        // set
//        http.authenticationManager(authenticationManager());

        // access denied exception handler , this is FilterInvocation level, not method level.
        http.exceptionHandling(e -> e.accessDeniedHandler(
                (request, response, accessDeniedException) -> {
                    String errorMessage = "Forbidden: No permission for: " + request.getMethod() + " " + request.getRequestURI();
                    log.error(errorMessage);
                    response.setStatus(HttpStatus.FORBIDDEN.value());
                    response.getOutputStream().println(errorMessage);
                }
        ));

        // Require Explicit Saving of SecurityContextRepository
        http.securityContext((securityContext) -> securityContext
                .securityContextRepository(new HttpSessionSecurityContextRepository())
        );
        return http.build();
    }

}
